package bsh;

import java.util.Collection;
import java.util.List;

import bsh.meta.FieldDescriptor;
import bsh.meta.MethodDescriptor;
import bsh.meta.TypeTransformation;
import bsh.meta.TypeWrapper;
import bsh.utils.reflection.transform.TypeTransformationFactory;

/**
 * Instances of this class deals with types while compiling (and not during runtime)
 * 
 * @author takeshi
 */
public interface ReflectionUtils {

    int MAX_DEPTH = 10;

    /**
     * Finds a method given the parameters and names
     * 
     * @param declaring {@link TypeWrapper}
     * @param name String
     * @param argTypes {@link TypeWrapper}
     * @return {@link MethodDescriptor} ou null se nenhum encontrado
     */
    MethodDescriptor findMethodForTypes(TypeWrapper declaring, String name, TypeWrapper[] argTypes);

    /**
     * Finds a constructor with the given signature
     * 
     * @param declaring {@link TypeWrapper}
     * @param argTypes {@link TypeWrapper}
     * @return lista com os argumentos ou null
     */
    List<? extends TypeWrapper> findConstructorFor(TypeWrapper declaring, TypeWrapper[] argTypes);

    /**
     * Tries to resolve a type given a name.
     * 
     * @param name String
     * @return {@link TypeWrapper} or <code>null</code> if none can be found
     */
    TypeWrapper resolveType(String name);

    /**
     * Finds the best match amongst the given candidates
     * 
     * @param candidates the candidates
     * @param match the actual argument types
     * @return the index of the best match or -1 if none
     */
    int findBestMatch(List<List<? extends TypeWrapper>> candidates, TypeWrapper[] match);

    /**
     * Checks if a given type is represented as an int in bytecode form
     * 
     * @param type {@link TypeWrapper}
     * @return boolean
     */
    boolean isIntPrimitive(TypeWrapper type);

    /**
     * Finds a required field on the given type. Throws an exception if none can be found
     * 
     * @param targetType tipo
     * @param field nome do campo
     * @return {@link FieldDescriptor}
     */
    FieldDescriptor findRequiredField(TypeWrapper targetType, String field);

    /**
     * Verifies if the given type is a boolean
     * 
     * @param type {@link TypeWrapper}
     * @return boolean
     */
    boolean isBooleanPrimitive(TypeWrapper type);

    /**
     * Finds a chan of transformations that can be applied to a given type, in order to adapt it to a target type, with a maximum
     * transformation depth
     * 
     * @param fromType {@link TypeWrapper}
     * @param toType {@link TypeWrapper}
     * @param maxDepth int
     * @return list or null if it cannot be found
     */
    List<TypeTransformation> findTransformationMatching(TypeWrapper fromType, TypeWrapper toType, int maxDepth);

    /**
     * Calls {@link #findTransformationMatching(TypeWrapper, TypeWrapper, int)} with {@value #MAX_DEPTH}
     * 
     * @param fromType {@link TypeWrapper}
     * @param toType {@link TypeWrapper}
     * @return list ou null se nenhum encontrado
     */
    List<TypeTransformation> findTransformationMatching(TypeWrapper fromType, TypeWrapper toType);

    /**
     * Works just like {@link #findTransformationMatching(TypeWrapper, TypeWrapper)} but adds more transformations
     * 
     * @param fromType {@link TypeWrapper}
     * @param toType {@link TypeWrapper}
     * @param factories {@link TypeTransformationFactory}
     * @return {@link List} de {@link TypeTransformation}
     */
    List<TypeTransformation> findTransformationMatchingIncluding(TypeWrapper fromType, TypeWrapper toType,
            TypeTransformationFactory... factories);

    /**
     * Returns applicable transformations for a given type
     * 
     * @param methodTargetType {@link TypeWrapper}
     * @return Collection
     */
    Collection<TypeTransformation> findApplicableTransformations(TypeWrapper methodTargetType);

    /**
     * Returns any method with the given name, declared on the given type. Must only be called if there are no ambiguities.
     * 
     * @param class1 declaring type
     * @param string name
     * @return {@link MethodDescriptor}
     */
    MethodDescriptor getMethodWithName(Class<?> class1, String string);

    /**
     * Verifies if the given object is an enum instance
     * 
     * @param obj Object
     * @return boolean
     */
    boolean isEnumInstance(Object obj);

    /**
     * Devolve o tipo de um enum dado algum dos seus valores
     * 
     * @param instance Object
     * @return Class
     */
    Class<?> getEnumType(Object instance);

    /**
     * Finds a field on a given type, returns null if none can be found
     * 
     * @param targetType the target type
     * @param field the field name
     * @return {@link FieldDescriptor} or null
     */
    FieldDescriptor findField(TypeWrapper targetType, String field);

    /**
     * Attempts to find an exact method match on the given type. Returns null if none can be found
     * 
     * @param type the declaring type
     * @param methodName the method name
     * @param typeWrappers the parameter types
     * @return {@link MethodDescriptor}
     */
    MethodDescriptor findExactMatch(TypeWrapper type, String methodName, TypeWrapper[] typeWrappers);

}
